BASIC OF C(11-19)

문자
%c
아스키 코드(Ascii Code)를 따름
0~127중 1바이트로 구성되며 주요 문자를 출력
ex)
0    48
A    65
a    97

한글은 유니코드에 포함
문자 입출력 함수
getchar()    단하나의 문자를 입력 받음

Buffer(버퍼)
버퍼란 임시적으로 특정한 데이터를 저장하기 위한 목저으로 사용
C프로그램은 기본적으로 사용자가 의도하지 않아도 자동으로 버퍼를 이용해 입출력을  처리함
입력버퍼로 인한 흔한 오류
#define _CRT_SECURE_NO_WARNINGS
#inlcude<stdio.h>
#include<stdlib.h>
int main(){
int a;
char c;
scaf("%d",&a);
printf("%d\n",a);
scaf("%c",&c);) // Enter
printf("%c\n",c);
system("pause");
return 0;
}
남아있는 입력 버퍼를 지우는 코드
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main(void){
int a; char c;
scaf("%d",&a);
printf("%d\n",a);
int temp;
//
while((temp=getchar())!=EOF && temp!='\n'){}
scaf("%c",&c);
printf("%c\n",c);
system(pause);
return 0;
}
문자열
문자들의 배열
문자열은 컴퓨터 메모리 구조상에서 마지막에 NULL(\0) 값을 포함

printf()와 같은 함수는 %s를 통해서 문자열을 출력할 때 내부적으로 NULL 값을 만날 때까지 출력.
문자열과 포인터
문자열 형태로 포인터를 사용하면 포인터에 특정한 문자열의 주소를 넣게 된다.
컴파일러가 이와 같은 문자열을 읽기 전용으로 메모리 공간에 넣은 뒤에 그 위치를 처리한다.

이와 같은 문자열을 “문자열 리터널” 이라고 한다.(변경 불가능함)
#include<stdio.h>
int main(void){
char *a="Hello world"; // NULL
printf("%s\n", a);
system("pause");
return 0;
}
문자열 입출력 함수
scanf() 함수는 공백을 만날 때 까지 입력을 받지만 gets() 함수는 공백까지 포함하여 한줄을 입력 받는다.

gets() 함수 또한 배열의 공간의 크기를 고려하지 않는다는 점에서 보안상의 취약점이 존재한다.
(버퍼의 크기를 벗어나도 입력을 받아버림)

gets_s(a, sizeof(a)) 대신 사용(C11) 부터
(버퍼의 범위를 넘으면 그즉시 Runtime 오류가 발생)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void){
char a[100];
gets_s(a,sizeof(a));
printf("%s\n",a);
system("pause");
return 0;
}
문자열 함수<string.h>
strlen()
strcmp()        문자열1이 문자열2보다 사전적을 앞을 있으면 -1, 뒤에 있으면 1을 반환
strcpy()
strcat()        문자열1에 문자열2를 더합니다.
strstr()        문자열1에 문자열2가 어떻게 포함되어 있는지를 반환
#include<stdio.h>
#include<string.h>
int main(void){
char a[20]="I like you";
char a[20]="like";
printf(" : %s\n",strstr(a,b));
system("pause");
return 0;
}
strstr은 공통된 문자열의 시작 주소 값을 반환받음. 따라서 printf()에서 %s는 NULL 까지인 like you를 출력
컴퓨터가 변수를 처리하는 방법
프로그램 메모리 주소

컴퓨터에서 프로그램이 실행되기 위해서 프로그램이 메모리에 적재(load)
일반적인 운영체제는 메모리 공간을 네가지로 구분하여 관리

코드        소스코드
데이터        전역변수, 정적변수
힙        동적 할당 변수
스택        지역 변수, 매개변수
전역변수(Global variable)
프로그램의 어디서든 접근 가능
main 함수가 실행되기도 전에 프로그램의 시작과 동시에 데이터 영역에 할당
프로그램의 크기가 커질수록 전역 변수로 인해 프로그램이 복잡해질 수도 있음
지역변수(Local variable)
프로그램의 특정 Block에서만 접근 가능 변수
함수가 실행될 때마다 스택 영역에 할당(함수가 종료되면 메모리에서 해제)
정적변수(Static variable)
특정한 block에서만 접근할 수 있는 변수(local variable)
프로그램이 시작될 때 데이터 영역에 할당, 프로그램이 종료되면 메모리에서 해제(global variable)

지역변수와 지역변수의 성격을 모두 가짐
레지스터 변수(Register variable)
메인메모리 대신 CPU 레지스터를 사용하는 변수
레지스터는 매우 한정되어 있어, 실제로 레지스터에서 처리될 지는 장담할 수 없음
#include<stdio.h>
int a=5; //
void process(){
static int c=5; // (process but )
c=c++;
printf("%d\n",c);
}
int main(void){
int b=7; //(main )
if(1){
int b=5; //(if )
}
printf("%d\n",a); //if block b main block b=7
process(); // 6
process(); // 7
register int d; //
system("pause");
return 0;
}
함수의 매개변수가 처리될 때
함수를 호출할 때 함수에 필요한 데이터를 매개변수로 전달
전달방식 1) 값에 의한 전달 2) 참조에 의한 전달

값에 의한 전달은 값을 전달하기 때문에 함수 내에서 변수가 새롭게 생성됨
참조에 의한 전달 방식은 주소를 전달함으로 원래의 변수 자체에 접근할 수 있다.(전역변수처럼 사용 가능)
#include<stdio.h>
#include<stdlib.h>
void add1(int a){ //
a=a+10;
}
void add2(int *a){ //
(*a)=(*a)+10;
}
int main(void){
int a=7;
add1(a);
printf("%d\n",a); //7
add2(a);
printf("%d\n",a); //17
system("pause");
return 0;
}
다차원 배열과 포인터 배열
2차원 배열 행렬 데이터 표현할 때 그래프 알고리즘을 처리할 떄 다수의 실생활 데이터를 처리할 때 사용

이차원 이상도 표현 가능
int a[10][10]={{value1,value2, ... },{value3,value4,...}} //
a[0][0] a[0][1] a[0][2]
a[1][0]  a[1][1] a[1][2]
2중 for 문과 함께 많이 사용된다.
#include<stdio.h>
#include<stdlib.h>
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int main(void){
int i,j;
for (i=0;i<3;i++){
fir(j=0;j<3;j++){
printf("%d\n",a[i][j]);
}
printf("\n");
}
system("pause");
return 0;
}
배열은 포인터와 거의 동일하게 동작한다
(포인터는 변수이며, 배열의 이름은 상수이다. 이점을 제외하고는 동일하다)
따라서 배열을 포인터처럼 사용하는 것은 무리가 있지만 포인터를 배열처럼 사용하는 것은 자유롭다
int a[10];
int b[10];
b=&a; //
int *b=&a // .
int *b=&a[0]
포인터는 연산을 통해 자료형의 크기만큼씩 이동한다. 정수(int)형의 경우 5바이트(byte)씩 이동
a[i]=*(a+i)
배열이나 포인터에서 a는 동일하게 주소를 말한다.
#include<stdio.h>
#incldue<stdlib.h>
int main(void){
int a[5]={1,2,3,4,5};
int *p=a;
printf("%d\n",*(p++)); // print 1
printf("%d\n",*(++p)); // print 3
printf("%d\n",*(p+2)); // print 5
system("pause");
return 0;
}
2차 배열 포인터로 처리하기
#include<stdio.h>
#include<stdlib.h>
int main(void){
int a[2][5]={{1,2,3,4,5},{6,7,8,9,10}};
int (*p)[5]=a[1]; // a 1{6,7,8,9,10}
int i;
for(i=0;i<5;i++){
printf("%d\n",p[0][i]);
}
system("pause");
return 0;
}
from C언어 입문(DO IT!)
배열을 기준으로 포인터와 합체하기 ( char *p[5] )
포인터를 기준으로 배열과 함체하기 ( char (*p)[5] )
char *p[5]; // same as char *p1, *p2, *p3, *p4, *p5
char (*p)[5]; // char[5]
char data[3][5];
char (*p)[5];
p=data;
(*p)[1]=3; // p[0][1]=3;
(*(p+1))[2]=4; // p[1][2]=4;
(*(p+2))[4]=5; // p[2][4]=5;
동적메모리 할당
동적은 프로그램 실행 도중에 라는 의미, 힙 영역에 저장된다.
일반적으로 C언어에서 배열의 경우 적절한 크기만큼 할댕해 주어야 한다.
우리가 원하는 만큼 메모리를 할당해서 사용하고자 한다면 동적 메모리를 할당하면 된다.

전통적인 C언어는 스택에 선언된 변수는 따로 메모리 해제를 해주지 않아도 된다.
반면에 동적 메모리의 경우에는 free() 함수를 이용해서 메모리 반드시 해제 해주어야 함

메모리 LEAK이 발생하지 않도록 유의
malloc()
#include<stdlib.h>
메모리 할당을 성공하면 주소를 반환하고, 그렇지 않으면 NULL을 반환
#include<stdio.h>
#include<stdlib.h>
int main(void){
int *a=malloc(sizeof(int));
printf("%d\n",a);
free(a);
a=malloc(sizeof(int));
printf("%d\n",a);
free(a)
system("pause");
return 0;
}
동적으로 문자열 처리하기
<string.h>에 포함
일관적인 범위의 메모리를 모두 특정한 값으로 설정하기 위해서 memset()을 사용
memset(포인터, 값, 크기);
#include<stdio.h>
#include<stdlib.h>
#inlcude<string.h>
int main(void){
char *a=malloc(100);
memset(a,'A',100);
for(int i=0;i<100;i++){
printf("%c ",a[i]);
}
system("pause");
return 0;
}
동적 메모리 할당 예제
#include<stdio.h>
#include<stdlib.h>
int main(void){
int **p=(int**)malloc(sizeof(int*)*3); //
for(int i=0;i<3;i++){
*(p+i)=(int*)malloc(sizeof(int)*3); //
} // 3*3 ;
for(int i=0;i<3;i++){
for (int j=0;j<3;j++){
*(*(p+i)+j)=i*3+j;
}
}
for(int i=0;i<3;i++){
for (int j=0;j<3;j++){
printf("%d ",*(*(p+i)+j));
}
printf("\n");
}
system("pause");
return 0;
}
함수 포인터
함수 또한 내부적으로 함수의 이름을 주고 받을 때 주소를 이용한다.

함수 포인터는 특정한 함수의 반환 자료형을 지정하는 방식으로 선언할 수 있다.
함수 포인터를 이용하면 형태가 같은 서로 다른 기능의 함수를 선택적으로 사용할 수 있다.
(*)()=;
매개변수 및 반환 자료형이 없는 함수 포인터
#include<stdio.h>
#include<stdlib.h>
void myFunction(){
printf("It's my function.\n");
}
void yourFunction(){
printf("It's your function.\n");
}
int main(void){
void (*fp)()=myFunction;
fp(); //myFunction()
fp=yourFunction;
fp(); //yourFunction()
system("pause");
return 0;
}
이와 같이 동일한 명령어 fp()로 다른 함수를 실행할 수 있다.
매개변수 및 반환 자료형이 있는 함수 포인터
#include<stdio.h>
#include<stdlib.h>
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
int main(void){
int (*fp)(int,int)=add;
printf("%d\n",fp(10,3));
fp=sub;
printf("%d\n",fp(10,3));
system("pause");
return 0;
}
함수 포인터를 반환하여 사용하기
#include<stdio.h>
#include<stdlib.h>
int add(int a, int b){
return a+b;
}
int (*process(char* a))(int,int){
printf("%s\n",a);
return add;
}
int main(void){
printf("%d\n",process("10 20 .")(10,20));
system("pause");
return 0;
}
구조체
여러개의 변수를 묶어 하나의 객체를 표현하고자 할 때 사용
캐릭터 몬스터 학생 좌표 등 다양한 객체를 정의할 때 사용
struct {
1 1;
2 2;
...
};
struct ; //
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Student {
char studentId[10];
char name[10];
int grade;
char major[51];
};
int main(void){
struct Student s;
strcpy(s.studentId,"2017103274");
strcpy(s.name,"");
s.grade=3;
strcpy(s.major,"");
printf(": %s\n",s.studentId);
printf(": %s\n",s.name);
printf(": %d\n",s.grade);
printf(": %d\n",s.major);
system("pause");
return 0;
}
특징
struct Student {
char studentId[10];
char name[10];
int grade;
char major[51];
}s; // .
struct Student s={"2017103274", "", 3, ""}; //
typedef struct Student{ //Student
char studentId[10];
char name[10];
int grade;
char major[51];
}Student; //typedef
구조체가 포인터 변수로 사용되는 경우
Student *s=malloc(sizeof(Student));
strcpy(s->studentId,"2017103274"); // ->
s->studentId
same as
(*s).studentId
파일 입출력
프로그램이 꺼진 후에도 데이터를 저장하기 위해서는 파일 입출력이 필요하다.

파일 열고 닫기
FILE 형식의 포인터 변수로 선언
fopen() fclose()

파일 입출력은 열고, 읽고/쓰고, 닫기의 과정을 철저히 따라야 한다.
파일을 열때는 동적으로 파일 포인터가 할당된다.
따라서 파일 처리 후 파일을 닫아주지 않으면 메모리 누수가 발생

파일 입출력 또한 불안정한 함수로 인식됨
fopen()
기본경로는 현재 프로그램의 경로이다.
가장 많이 사용되는 접근 방식은 다음과 같다
r        파일에 접근하여 데이터를 읽기
w        파일에 접근하여 데이터 쓰기
a        파일에 접근해서 데이터를 뒤에서부터 기록
fprintf(), fscanf()
fprintf( , , );
fscanf( , , );
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main(void){
char s[20]="Hello World";
FILE *fp;
fp=fopen("temp.txt","w");
fprintf(fp,"%s\n",s);
fclose(fp);
return 0;
}
예제
input.txt
5
98
84
89
95
97
#define _CRT_NO_SECURE_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct {
char name[20];
int score;
}Student;
int main(void){
int n, sum=0;
FILE *fp;
fp=fopen("input.txt","r");fscanf(fp,"%d",&n);
Student *students=(Student*)mallco(sizeof(Student)*n);
for(int i=0;i<n;i++){
fscanf(fp,"%s %d",&((students+i)->name),&((students+i)->score));
}
for(int i=0;i<n;i++){
sum+=(students+i)->score;
}
free(students);
fclose(fp);
printf(" : %.2f\n",(double)sum/n);
system("pause");
return 0;
}
전처리기
전처리 구문은 다른 프로그램 영역과 독립적으로 처리된다.
전처리기 구문은 소스코드 파일 단위로 효력이 존재
#include
가장 많이 사용되는 문법
특정한 파일을 라이브러리로서 포함시키기 위해 사용
include구문으로 가져 올수 있는 파일에는 제약이 없다.

#include<파일이름>
시스템 디렉토리에서 파일을 검색한다.
운영체제마다 시스템 디렉토리가 존재하는 경로가 다를 수 있다.

#include”파일이름”
현재 폴더에서 파일을 먼저 검색한다.
만약 현재 폴더에 파일이 없다면, 시스템 디렉토리에서 파일을 검색한다.
header 파일을 만들 때, 의도치 않게 한번 참조한 파일을 여러번 참조하지 않도록 주의
매크로 전처리기
프로그램에서 사용되는 상수나 함수를 매크로 형태로 저장하기 위해 사용한다
#define 문법을 사용해 정의할 수 있다

매크로 전처리기가 함수 형태로 사용될 수 있게 하기 위해서 인자가 포함될 수 있다
#define POW(x) (x*x)
#include<stdio.h>
int main(void){
int x=10;
printf("x : %d\n",POW(x));
return 0;
}
#define swap(type,x,y) do{type t=x;x=y;y=t;}while(0)
조건부 컴파일
컴파일이 이루어지는 영역을 지정하는 기법
일반적으로 디버깅과 소스코드 이식을 목적으로 하여 작성된다.
C언어로 시스템 프로그램을 작성할 때에는 운영체제에 따라서 소스코드가 달라질 수 있다.
운영체제에 따라 컴파일이 수행되는 소스코드를 다르게 할 수 있다.
이식성을 높여줄 수 있음

#ifndef ~ #endif
파일 분할 컴파일
일반적으로 자신이 직접 라이브러리를 만들 때에는 C언어 파일과, 헤더 파일을 모두 작성해야 한다.

C언어 파일과 헤더 파일을 완전히 분리 시킴
main.c
#include<stdio.h>
#include"temp.h"
int main(void){
printf("%d\n",add(3,5));
system("pause");
return 0;
}
temp.h
#ifndef _TEMP_H_
#define _TEMP_H_
int add(int a, int b);
#endif
temp.c
#include"temp.h" //temp.h
int add(int a, int b){
return a+b;
}